package com.nulldev.util.concurrency.threadIt.v4.emapi.uft;

import java.util.Queue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import com.nulldev.util.concurrency.threadIt.v4.asyncTasks.AsyncTaskCallback;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.io.FastIO.FastIO;

public class ExecutableUtils {

	public static final boolean ENABLE_FINAL_FAIL_ALL_TASKS_APPROACH = true;
	public static final boolean ENABLE_NEW_ASYNC_FAILURE_LOGIC = true;

	/* FT: Project Fix-a-Lot */

	public static <T> void callAsyncFails(final boolean ENABLE_OFFLOADED_ASYNC, final boolean ENABLE_ASYNC_EVENT_SUBMISSION,
			final Queue<AsyncTaskCallback<T>> callback, final Throwable e) {
		if (callback.isEmpty()) {
			FastIO.get().printlnError(e);
			return;
		} else {
			if (ENABLE_OFFLOADED_ASYNC) {
				AsyncTaskCallback<T> _o;
				while ((_o = callback.poll()) != null) {
					final AsyncTaskCallback<T> o = _o;
					ExecutorManager.get().autoAsyncSubmit(ENABLE_ASYNC_EVENT_SUBMISSION, () -> {
						try {
							o.onError(e);
						} catch (Throwable ec) {
							FastIO.get().printlnError(ec);
						}
					});
				}
			} else {
				AsyncTaskCallback<T> o;
				while ((o = callback.poll()) != null) {
					try {
						o.onError(e);
					} catch (Throwable ec) {
						FastIO.get().printlnError(ec);
					}
				}
			}
		}
	}

	public static <T> void callAsyncEventsOrFail(final boolean ENABLE_OFFLOADED_ASYNC, final boolean ENABLE_ASYNC_EVENT_SUBMISSION,
			final Queue<AsyncTaskCallback<T>> callback, final T r) {
		if (callback == null || callback.isEmpty()) {
			return;
		}

		final AtomicBoolean shouldStop = new AtomicBoolean();
		final AtomicReference<Throwable> failedBy = new AtomicReference<Throwable>();
		if (ENABLE_OFFLOADED_ASYNC) {
			AsyncTaskCallback<T> _o;
			while ((_o = callback.poll()) != null && !shouldStop.get()) {
				final AsyncTaskCallback<T> o = _o;
				ExecutorManager.get().autoAsyncSubmit(ENABLE_ASYNC_EVENT_SUBMISSION, new Runnable() {

					@Override
					public void run() {
						try {
							o.onCompletion(r);
						} catch (Throwable e) {
							if (ENABLE_FINAL_FAIL_ALL_TASKS_APPROACH) {
								shouldStop.set(true);
								failedBy.lazySet(e);

								/*
								 * Send back into the callback queue, if this somehow fails, risk it and
								 * directly call it.
								 */
								if (!callback.offer(o))
									o.onError(e);
							} else if (ENABLE_NEW_ASYNC_FAILURE_LOGIC) {
								o.onError(e);
							} else {
								FastIO.get().printlnError(e);
							}
						}
					}
				});
			}

			if (shouldStop.get() && ENABLE_FINAL_FAIL_ALL_TASKS_APPROACH) {
				callback.add(_o);
				callAsyncFails(ENABLE_OFFLOADED_ASYNC, ENABLE_ASYNC_EVENT_SUBMISSION, callback,
						new RuntimeException("Failed to run asynchronous events.", failedBy.get()));
			}
		} else {
			AsyncTaskCallback<T> o;
			while ((o = callback.poll()) != null) {
				try {
					o.onCompletion(r);
				} catch (Throwable e) {
					if (ENABLE_FINAL_FAIL_ALL_TASKS_APPROACH) {
						shouldStop.set(true);
						failedBy.lazySet(e);

						/*
						 * Send back into the callback queue, if this somehow fails, risk it and
						 * directly call it.
						 */
						if (!callback.offer(o))
							o.onError(e);
						break;
					} else if (ENABLE_NEW_ASYNC_FAILURE_LOGIC) {
						o.onError(e);
					} else {
						FastIO.get().printlnError(e);
					}
				}
			}
			if (shouldStop.get() && ENABLE_FINAL_FAIL_ALL_TASKS_APPROACH) {
				callback.add(o);
				callAsyncFails(ENABLE_OFFLOADED_ASYNC, ENABLE_ASYNC_EVENT_SUBMISSION, callback,
						new RuntimeException("Failed to run asynchronous events.", failedBy.get()));
			}
		}
	}

}
